package net.maku.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import net.maku.api.module.storage.StorageService;
import net.maku.convert.PayItemConvert;
import net.maku.entity.PayedDoneItemEntity;
import net.maku.enums.AuditStateEnum;
import net.maku.enums.PayStateEnum;
import net.maku.excel.PayItemEntity;
import net.maku.excel.SmartPayDataReport;
import net.maku.excel.SmartPayDataResult;
import net.maku.excel.SmartPayTemplateData;
import net.maku.framework.common.exception.FastException;
import net.maku.framework.common.page.PageResult;
import net.maku.framework.common.service.impl.BaseServiceImpl;
import net.maku.convert.WillPayItemConvert;
import net.maku.entity.WillPayItemEntity;
import net.maku.framework.common.utils.ExcelUtil;
import net.maku.framework.security.user.SecurityUser;
import net.maku.query.WillPayItemAuditQuery;
import net.maku.query.WillPayItemDashBoardQuery;
import net.maku.query.WillPayItemModifyQuery;
import net.maku.query.WillPayItemQuery;
import net.maku.service.PayedDoneItemService;
import net.maku.vo.*;
import net.maku.dao.WillPayItemDao;
import net.maku.service.WillPayItemService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 应付款管理
 *
 * @author 中国华 hualaihu@126.com
 * @since 1.0-SNAPSHOT 2022-08-29
 */
@Service
@AllArgsConstructor
public class WillPayItemServiceImpl extends BaseServiceImpl<WillPayItemDao, WillPayItemEntity> implements WillPayItemService {

    private final PayedDoneItemService payedDoneItemService;
    private final StorageService storageService;

    @Override
    public PageResult<WillPayItemVO> page(WillPayItemQuery query) {

        LambdaQueryWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(WillPayItemEntity::getAuditState, AuditStateEnum.AUDIT_OK.getValue())
                .ge(query.getMinTotalPrice() != null, WillPayItemEntity::getTotalPrice, query.getMinTotalPrice())
                .le(query.getMaxTotalPrice() != null, WillPayItemEntity::getTotalPrice, query.getMaxTotalPrice())
                .in(!CollectionUtils.isEmpty(query.getEmergencyDegrees()), WillPayItemEntity::getEmergencyDegree, query.getEmergencyDegrees())
                .ge(query.getStartCreateTime() != null, WillPayItemEntity::getCreateTime, query.getStartCreateTime())
                .le(query.getEndCreateTime() != null, WillPayItemEntity::getCreateTime, query.getEndCreateTime());


        if (StringUtils.hasText(query.getKeyword())){
            //查询审批编号或项目名称或供应商名称或者材料名称
            wrapper.and(qw -> qw.like( WillPayItemEntity::getSeq, query.getKeyword()).or()
                    .like(WillPayItemEntity::getProjectName, query.getKeyword()).or()
                    .like(WillPayItemEntity::getSupplier, query.getKeyword()).or()
                    .like(WillPayItemEntity::getMaterialName, query.getKeyword()));
        }

        dataScopeWrapper(wrapper);

        IPage<WillPayItemEntity> page = baseMapper.selectPage(getPage(query), wrapper);

        return new PageResult<>(WillPayItemConvert.INSTANCE.convertList(page.getRecords()), page.getTotal());
    }

    @Override
    public PageResult<waitAuditItem> auditPage(WillPayItemAuditQuery query) {

        LambdaQueryWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(WillPayItemEntity::getAuditState, AuditStateEnum.AUDIT_WAIT.getValue());
        if (StringUtils.hasText(query.getKeyword())){
                    //查询审批编号或项目名称或供应商名称或者材料名称
            wrapper.and(qw -> qw.like( WillPayItemEntity::getSeq, query.getKeyword()).or()
                    .like(WillPayItemEntity::getProjectName, query.getKeyword()).or()
                    .like(WillPayItemEntity::getSupplier, query.getKeyword()).or()
                    .like(WillPayItemEntity::getMaterialName, query.getKeyword()));
        }
        dataScopeWrapper(wrapper);

        IPage<WillPayItemEntity> page = baseMapper.selectPage(getPage(query), wrapper);

        return new PageResult<>(WillPayItemConvert.INSTANCE.waitAuditConvertList(page.getRecords()), page.getTotal());
    }

    @Override
    public PageResult<waitModifyItem> modifyPage(WillPayItemModifyQuery query) {
        LambdaQueryWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(WillPayItemEntity::getAuditState, AuditStateEnum.AUDIT_REFUSE.getValue());
        if (StringUtils.hasText(query.getKeyword())){
            //查询审批编号或项目名称或供应商名称或者材料名称
            wrapper.and(qw -> qw.like( WillPayItemEntity::getSeq, query.getKeyword()).or()
                    .like(WillPayItemEntity::getProjectName, query.getKeyword()).or()
                    .like(WillPayItemEntity::getSupplier, query.getKeyword()).or()
                    .like(WillPayItemEntity::getMaterialName, query.getKeyword()));
        }
        dataScopeWrapper(wrapper);

        IPage<WillPayItemEntity> page = baseMapper.selectPage(getPage(query), wrapper);

        return new PageResult<>(WillPayItemConvert.INSTANCE.modifyAuditConvertList(page.getRecords()), page.getTotal());
    }

    private LambdaQueryWrapper<WillPayItemEntity> getWrapper(WillPayItemQuery query){
        LambdaQueryWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaQuery();

        return wrapper;
    }


    @Override
    public void save(WillPayItemAddVO vo) {
        //检查seq是否存在
        WillPayItemEntity payEntity = getBySeq(vo.getSeq());
        if(payEntity != null) {
            throw new FastException("审批编号已经存在");
        }

        PayedDoneItemEntity done = payedDoneItemService.getBySeq(vo.getSeq());
        if(payEntity != null) {
            throw new FastException("审批编号已经存在");
        }

        WillPayItemEntity entity = WillPayItemConvert.INSTANCE.addConvertEntity(vo);

        if (PayStateEnum.PAY_FINISH.getValue().equals(vo.getPayState())){
            entity.setPayedPrice(entity.getTotalPrice());
        }

        baseMapper.insert(entity);
    }

    @Override
    public void update(WillPayItemUpdateVO vo) {
        //查询seq是否冲突
        if (StringUtils.hasText(vo.getSeq())){
            WillPayItemEntity payEntity = getBySeq(vo.getSeq());
            if(payEntity != null && !payEntity.getId().equals(vo.getId())) {
                throw new FastException("审批编号已经存在");
            }

            PayedDoneItemEntity done = payedDoneItemService.getBySeq(vo.getSeq());
            if(done != null) {
                throw new FastException("审批编号已经存在");
            }
        }

        WillPayItemEntity entity = WillPayItemConvert.INSTANCE.updateConvertEntity(vo);

        //当已付款修改为未付款或不用付款时，需要修改实际付款记录
        if (vo.getPayState() != null &&
                (vo.getPayState().equals(PayStateEnum.PAY_WAIT) ||
                vo.getPayState().equals(PayStateEnum.PAY_NEEDLESS))
               ){
            WillPayItemEntity payEntity = getById(vo.getId());
            if (payEntity.getPayState().equals(PayStateEnum.PAY_FINISH)){
                entity.setPayedPrice(new BigDecimal(0));
            }
        }
        updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> idList) {
        removeByIds(idList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditBatch(List<WillPayItemAuditVO> vo) {
        List<WillPayItemEntity> willPayItems = WillPayItemConvert.INSTANCE.auditConvertList(vo);

        willPayItems = willPayItems.stream().map(a->{
            a.setAuditTime(new Date());
            return a;
        }).collect(Collectors.toList());

        updateBatchById(willPayItems);

        List<Long> ids = vo.stream().map(WillPayItemAuditVO::getId).collect(Collectors.toList());

        //如果是不用付款，则需要将记录移到pay_done_item
        baseMapper.moveToPayDoneItem(ids);

        //删除不用付款记录
        baseMapper.deletePaylessItem(ids);
    }

    @Override
    public void batchSetDegree(List<WillPayItemSetDegreeVO> vo) {
        List<WillPayItemEntity> willPayItems = WillPayItemConvert.INSTANCE.setDegreeConvertList(vo);
        updateBatchById(willPayItems);
    }

    @Override
    public void batchSetPayed(List<Long> ids) {
        //更新为已付款
        LambdaUpdateWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaUpdate();
        wrapper.set(WillPayItemEntity::getPayState, PayStateEnum.PAY_FINISH.getValue())
                .in(WillPayItemEntity::getId, ids);
        update(wrapper);

        //需要将记录移到pay_done_item
        baseMapper.moveToPayDoneItem(ids);

        //删除付款记录
        baseMapper.deletePaylessItem(ids);
    }

    @Override
    public void setPayedPrice(Long id, BigDecimal payPrice) {

        //先查询目前已付金额
        WillPayItemEntity payItem = this.getById(id);

        BigDecimal totalPayedPrice = new BigDecimal(0);

        totalPayedPrice = payItem.getPayedPrice() == null ? payPrice:payItem.getPayedPrice().add(payPrice);
        if (totalPayedPrice.compareTo(new BigDecimal(0)) < 0){
            throw new FastException("实际已付金额不能小于0");
        }


        //更新为已付款
        LambdaUpdateWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaUpdate();
        wrapper.set(WillPayItemEntity::getPayedPrice, totalPayedPrice);
        if (totalPayedPrice.compareTo(payItem.getTotalPrice()) >= 0){
            wrapper.set(WillPayItemEntity::getPayState, PayStateEnum.PAY_FINISH.getValue());
        }
        wrapper.eq(WillPayItemEntity::getId, id);
        update(wrapper);

        if (totalPayedPrice.compareTo(payItem.getTotalPrice()) >= 0){

            List<Long> ids = Arrays.asList(id);

            //需要将记录移到pay_done_item
            baseMapper.moveToPayDoneItem(ids);

            //删除付款记录
            baseMapper.deletePaylessItem(ids);
        }

    }


    @Override
    public EmergencyDegreeStatsVO statsEmergencyDegree() {

        return baseMapper.statsEmergencyDegree();
    }


    @Override
    public List<WillPayItemDashBoardVO> dashboard(WillPayItemDashBoardQuery query) {

        LambdaQueryWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaQuery();

        wrapper.eq(WillPayItemEntity::getAuditState, AuditStateEnum.AUDIT_OK.getValue())
                .eq(query.getEmergencyDegree() != null, WillPayItemEntity::getEmergencyDegree, query.getEmergencyDegree());

        if (StringUtils.hasText(query.getKeyword())){
            //查询审批编号或项目名称或供应商名称或者材料名称
			wrapper.and(qw -> qw.like( WillPayItemEntity::getSeq, query.getKeyword()).or()
                    .like(WillPayItemEntity::getProjectName, query.getKeyword()).or()
                    .like(WillPayItemEntity::getSupplier, query.getKeyword()).or()
                    .like(WillPayItemEntity::getMaterialName, query.getKeyword()));
        }

        //应付款金额范围
        applyPriceRange(wrapper, query);

        List<WillPayItemEntity> list = baseMapper.selectList(wrapper);

        return WillPayItemConvert.INSTANCE.listConvertDashBoard(list);
    }

    @Override
    public String importExcel(MultipartFile file) {
        List<SmartPayTemplateData> smartPayDatas = ExcelUtil.readExcel(file, SmartPayTemplateData.class, 0, 1);
        SmartPayDataReport report = addSmartPayData(smartPayDatas);

        String resultFile = "";
        if (report.getFailedCount() > 0) {
            resultFile = createExcelResult(report);
        }

        return resultFile;
    }

    @Override
    public void exportAll(HttpServletResponse response) {

        List<PayItemEntity> allPayData = getAllPayData();
        if (CollectionUtils.isEmpty(allPayData)){
            throw new FastException("当前无记录可导出");
        }

        OutputStream outputStream = null;
        ExcelWriter excelWriter = null;
        InputStream is = null;
        try {
            outputStream = response.getOutputStream();
            is = this.getClass().getResourceAsStream("/template/smartpay_data.xlsx");
            excelWriter = EasyExcel.write(outputStream).withTemplate(is).build();
            WriteSheet writeSheet = EasyExcel.writerSheet().build();

            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            //3，往sheet表中填充数据：
            excelWriter.fill(allPayData, fillConfig, writeSheet);
            //4，设置输出流格式以及文件名：
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            String fileName = URLEncoder.encode("付款数据表", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            //5，最后，千万别忘记finish，关闭IO流；
            if (excelWriter != null) {
                excelWriter.finish();
            }

            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    public void setReback(Long id) {
        //更新为待审核
        LambdaUpdateWrapper<WillPayItemEntity> wrapper = Wrappers.lambdaUpdate();
        wrapper.set(WillPayItemEntity::getAuditState, AuditStateEnum.AUDIT_WAIT.getValue())
                .set(WillPayItemEntity::getAuditTime, null)
                .set(WillPayItemEntity::getAuditReason, null)
                .eq(WillPayItemEntity::getId, id);
        update(wrapper);
    }

    private List<PayItemEntity> getAllPayData(){
        //先获取未支付的
        List<WillPayItemEntity> payingList = this.list();
        List<PayItemEntity> payEntities = PayItemConvert.INSTANCE.payingConvertList(payingList);

        //获取已支付的
        List<PayedDoneItemEntity> payedList = payedDoneItemService.list();
        List<PayItemEntity> payedEntities = PayItemConvert.INSTANCE.payedConvertList(payedList);

        payEntities.addAll(payedEntities);
        return payEntities;
    }

    private String createExcelResult(SmartPayDataReport report) {

        // 没有目录，则自动创建目录
        String tempResultExcel =  "导入文件结果报告_" + SecurityUser.getUserId()  + ".xlsx";
        File file = new File(storageService.getUploadPath() + File.separator + tempResultExcel);
        File parent = file.getParentFile();
        if (parent != null && !parent.mkdirs() && !parent.isDirectory()) {
            throw new FastException("系统繁忙，请稍后再试");
        }

        InputStream is = this.getClass().getResourceAsStream("/template/smartpay_report.xlsx");
        ExcelWriter excelWriter = EasyExcel.write(storageService.getUploadPath() + File.separator + tempResultExcel).withTemplate(is).build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        HashMap<String, Integer> mapCount = new HashMap<>();
        mapCount.put("successCount", report.getSuccessCount());
        mapCount.put("failedCount", report.getFailedCount());

        excelWriter.fill(mapCount, writeSheet);
        excelWriter.fill(report.getSmartPayDataResult(), writeSheet);
        excelWriter.finish();

         return storageService.getUploadUrl() + tempResultExcel;

    }

    private SmartPayDataReport  addSmartPayData(List<SmartPayTemplateData> smartPayDatas) {
        if (CollectionUtils.isEmpty(smartPayDatas)){
            throw new FastException("您上传的文件里没有内容，请核实");
        }

        SmartPayDataReport report = new SmartPayDataReport();
        List<SmartPayDataResult> results = new ArrayList<>();

        List<String> payStateDescs = Arrays.asList("是", "否", "不用付");
        List<String> emergencyDegreeDescs = Arrays.asList("普通","加急","紧急", "特急");

        for (int i = 0; i < smartPayDatas.size(); i++) {
            StringBuffer errMsg = new StringBuffer();
            SmartPayTemplateData item = smartPayDatas.get(i);

            if (!StringUtils.hasText(item.getSeq())) {
                errMsg.append("【审批编号】必填\r\n");
            }
            if (!StringUtils.hasText(item.getSupplier())) {
                errMsg.append("【材料供应商】必填\r\n");
            }
            if (!StringUtils.hasText(item.getProjectName())) {
                errMsg.append("【项目名称】必填\r\n");
            }
            if (!StringUtils.hasText(item.getMaterialName())) {
                errMsg.append("【材料名称】必填\r\n");
            }

            if (!StringUtils.hasText(item.getTotalPriceDesc())) {
                errMsg.append("【应付账款金额】必填\r\n");
            }
            else if (!NumberUtil.isNumber(item.getTotalPriceDesc()) ||
                    (new BigDecimal(item.getTotalPriceDesc()).compareTo(new BigDecimal(0)) < 0)){
                errMsg.append("【应付账款金额】应该为大于0的数字\r\n");
            }

            if (!StringUtils.hasText(item.getPayStateDesc())) {
                item.setPayStateDesc("否");
            }
            else if (!payStateDescs.contains(item.getPayStateDesc())){
                errMsg.append("【是否已付】只能填写是、否、不用付\r\n");
            }

            if (!StringUtils.hasText(item.getEmergencyDegreeDesc())) {
                item.setEmergencyDegreeDesc("普通");
            }
            else if (!emergencyDegreeDescs.contains(item.getEmergencyDegreeDesc())){
                errMsg.append("【付款紧急程度】只能填写普通、加急、紧急、特急\r\n");
            }

            SmartPayDataResult result = new SmartPayDataResult();
            BeanUtil.copyProperties(item, result);

            if (0 == errMsg.length()){
                WillPayItemAddVO willPayItemAddVO = WillPayItemConvert.INSTANCE.excelConvertEntity(item);
                try{
                    save(willPayItemAddVO);
                }catch (FastException e){
                    errMsg.append("【审批编号】存在重复的审核编号");
                }
            }

            if (0 == errMsg.length()){
                report.setSuccessCount(report.getSuccessCount()+1);
                result.setResultFlag(1);
                errMsg.append("上传成功");
            }
            else{
                report.setFailedCount(report.getFailedCount()+1);
                result.setResultFlag(0);
            }

            result.setResultMsg(errMsg.toString());
            results.add(result);
        }

        report.setSmartPayDataResult(results);

        return report;

    }


    private void applyPriceRange(LambdaQueryWrapper<WillPayItemEntity> wrapper, WillPayItemDashBoardQuery query){
        if (CollectionUtils.isEmpty(query.getTotalPrices())){
            return;
        }

        StringBuffer priceRange = new StringBuffer();
        priceRange.append("  (");

        for (int i = 0; i < query.getTotalPrices().size(); i++){
            if (0 != i){
                priceRange.append(" or ");
            }

            WillPayItemDashBoardQuery.TotalPriceQuery priceRangeItem = query.getTotalPrices().get(i);

            if (priceRangeItem.getMinTotalPrice() != null && priceRangeItem.getMaxTotalPrice() != null){
                if (0 == priceRangeItem.getMinTotalPrice().compareTo(priceRangeItem.getMaxTotalPrice())){
                    priceRange.append(" total_price = ").append(priceRangeItem.getMinTotalPrice());
                }
                else{
                    priceRange.append(" (total_price >= ").append(priceRangeItem.getMinTotalPrice());
                    priceRange.append(" and total_price <= ").append(priceRangeItem.getMaxTotalPrice()).append(")");
                }
            }

            if (priceRangeItem.getMinTotalPrice() != null && priceRangeItem.getMaxTotalPrice() == null){
                priceRange.append(" total_price >= ").append(priceRangeItem.getMinTotalPrice());
            }

            if (priceRangeItem.getMaxTotalPrice() != null && priceRangeItem.getMinTotalPrice() == null){
                priceRange.append(" total_price <= ").append(priceRangeItem.getMaxTotalPrice());
            }
        }

        priceRange.append(" )");

        wrapper.apply(priceRange.toString());
    }



}